#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include "mutex_buffer_pool.h"
#include "sha256.h"
// #define using_piece_speed_up

#define MB (1 << 20)
const size_t single_buffer_size = piece_size;
const unsigned int buffer_count = 32;

mutex_buffer_pool* pool;

enum user {reading_user, processing_user};

clock_t reading_time = 0;
clock_t processing_time = 0;

clock_t reading_thread_time = 0;    // 线程的总时间
clock_t processing_thread_time = 0;

#define put_hex(x)              \
{                               \
    if(0 <= (x) && (x) <= 9)    \
        putchar((x) + '0');     \
    else                        \
        putchar((x) + 'a' - 10);\
}

void hex_print_byte(unsigned char x)
{
    unsigned char high_part = (x & 0xf0) >> 4;
    unsigned char low_part = x & 0xf;

    put_hex(high_part);
    put_hex(low_part);
}


void* reading_thread_kernel(void* argv)
{
    size_t read = 0;
    FILE* fp = (FILE*)argv;
    char* reading_buffer = NULL;
    clock_t start, end;
    clock_t total_start, total_end;
    total_start = clock();

    do
    {
        reading_buffer = get_buffer(pool, reading_user);
        start = clock();

        read = fread(
            reading_buffer,
            sizeof(char),
            single_buffer_size,
            fp
        );
        assign_size(pool, reading_user, read);
        end = clock();
        reading_time += end - start;

        release_buffer(pool, reading_user);
    } while(read >= single_buffer_size);
    total_end = clock();
    reading_thread_time += total_end - total_start;
}


void* processing_thread_kernel(void* argv)
{
    size_t processed = 0;
    size_t to_process_size = 0;
    sha256_context* context = (sha256_context*)argv;
    char* processing_buffer = NULL;
    clock_t start, end;

    do
    {
        processing_buffer = get_buffer(pool, processing_user);
        start = clock();

        to_process_size = get_real_size(pool, processing_user);

        #ifdef using_piece_speed_up    
            if(to_process_size == single_buffer_size)
                update_piece(context, processing_buffer);
            else
                update(context, processing_buffer, to_process_size);
        #else
            update(context, processing_buffer, to_process_size);
        #endif
        
        processed = to_process_size;
        end = clock();
        processing_time += end - start;

        release_buffer(pool, processing_user);
    } while(processed >= single_buffer_size);

    // printf("done processing.\n");
}

void print_sha256_digesthex(FILE* fp)
{
    unsigned int i;
    char* digest_array = malloc(sizeof(char) * 32);
    sha256_context* context = new_context();
    
    pool = new_mutex_buffer_pool(2, single_buffer_size, buffer_count);

    pthread_t reading_thread;
    pthread_t processing_thread;

    pthread_create(&reading_thread, NULL, reading_thread_kernel, fp);
    pthread_create(&processing_thread, NULL, processing_thread_kernel, context);

    pthread_join(reading_thread, NULL);
    pthread_join(processing_thread, NULL);

    digest(context);
    get_digest(context, digest_array);
    
    printf("SHA-256 = ");
    for(i = 0; i < 32; i++)
        hex_print_byte(digest_array[i]);
    
    printf("\nfile size = %llu Byte\n", context->length / 8);
    
    destroy_mutex_buffer_pool(pool);
    destroy_context(context);
    free(digest_array);
}


int main(int argc, char** argv)
{
    if(argc == 1)
    {
       printf("No file is inputing.");
       return 0;
    }
    clock_t start, end;

    // char* filename = "test_files\\temp_sha.txt";
    char* filename = argv[1];
    
    start = clock();
    FILE* fp = fopen(filename, "rb");
    print_sha256_digesthex(fp);
    fclose(fp);
    
    end = clock();
    printf("time used %d ms\n", end - start);
    printf("* reading time = %d ms\n", reading_time);
    printf("    * reading thread time = %d ms\n", reading_thread_time);
    printf("* processing time = %d ms\n", processing_time);
    return 0;
}
